package com.ruowei.modules.foodOrder.web.rest;

import com.querydsl.core.BooleanBuilder;
import com.querydsl.jpa.impl.JPAQueryFactory;
import com.ruowei.common.error.exception.CommonException;
import com.ruowei.modules.foodOrder.domain.*;
import com.ruowei.modules.foodOrder.repository.*;
import com.ruowei.modules.foodOrder.web.dto.MerchandiseSkuDTO;
import com.ruowei.modules.sys.repository.SysUserRepository;
import com.ruowei.modules.sys.utils.CommonUtils;
import com.ruowei.security.CurrentUser;
import com.ruowei.security.UserModel;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import springfox.documentation.annotations.ApiIgnore;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * REST controller for managing {@link MerchandiseSku}.
 */
@RestController
@RequestMapping("/api")
@Api(tags = "商品sku")
@Transactional
public class MerchandiseSkuResource {

    private final Logger log = LoggerFactory.getLogger(MerchandiseSkuResource.class);

    private final SysUserRepository sysUserRepository;
    private final MerchandiseSkuRepository merchandiseSkuRepository;
    private final MerchandiseAttributesRepository merchandiseAttributesRepository;
    private final AttributeOptionsRepository attributeOptionsRepository;
    private final SkuOptionsRelationshipRepository skuOptionsRelationshipRepository;
    private final MerchandiseRepository merchandiseRepository;
    private final JPAQueryFactory jpaQueryFactory;

    private final QMerchandiseSku qMerchandiseSku = QMerchandiseSku.merchandiseSku;
    private final QMerchandiseAttributes qMerchandiseAttributes = QMerchandiseAttributes.merchandiseAttributes;
    private final QAttributeOptions qAttributeOptions = QAttributeOptions.attributeOptions;
    private final QSkuOptionsRelationship qSkuOptionsRelationship = QSkuOptionsRelationship.skuOptionsRelationship;


    public MerchandiseSkuResource(SysUserRepository sysUserRepository, MerchandiseSkuRepository merchandiseSkuRepository, MerchandiseAttributesRepository merchandiseAttributesRepository, AttributeOptionsRepository attributeOptionsRepository, SkuOptionsRelationshipRepository skuOptionsRelationshipRepository, MerchandiseRepository merchandiseRepository, JPAQueryFactory jpaQueryFactory) {
        this.sysUserRepository = sysUserRepository;
        this.merchandiseSkuRepository = merchandiseSkuRepository;
        this.merchandiseAttributesRepository = merchandiseAttributesRepository;
        this.attributeOptionsRepository = attributeOptionsRepository;
        this.skuOptionsRelationshipRepository = skuOptionsRelationshipRepository;
        this.merchandiseRepository = merchandiseRepository;
        this.jpaQueryFactory = jpaQueryFactory;
    }


    @PostMapping("/setMerchandiseSku")
    @ApiOperation(value = "设置商品sku")
    public ResponseEntity<Void> setMerchandiseSku(@RequestBody List<MerchandiseSkuDTO> merchandiseSkuDTOS,
                                                  @ApiIgnore @CurrentUser UserModel userModel) {

        if (!merchandiseSkuDTOS.isEmpty()) {
            String merchandiseCode = merchandiseSkuDTOS.get(0).getMerchandiseCode();
            Optional<Merchandise> opt = merchandiseRepository.findOneByMerchandiseCodeAndDeleteStatus(merchandiseCode, false);
            if (!opt.isPresent()) {
                throw new CommonException("商品已删除");
            }
            if (opt.get().isPublishStatus()) {
                throw new CommonException("商品上架时无法修改商品规格");
            }
        }
        //删除默认sku
        jpaQueryFactory.update(qMerchandiseSku)
            .set(qMerchandiseSku.status, false)
            .where(qMerchandiseSku.skuCode.in(
                jpaQueryFactory.select(qSkuOptionsRelationship.skuCode)
                    .from(qSkuOptionsRelationship)
                    .leftJoin(qAttributeOptions).on(qAttributeOptions.optionCode.eq(qSkuOptionsRelationship.optionCode))
                    .leftJoin(qMerchandiseAttributes).on(qMerchandiseAttributes.attributeCode.eq(qAttributeOptions.attributeCode))
                    .where(qAttributeOptions.optionName.eq("默认")
                        .and(qMerchandiseAttributes.attributeName.eq("默认"))
                        .and(qMerchandiseAttributes.merchandiseCode.eq(merchandiseSkuDTOS.get(0).getMerchandiseCode())))
                    .fetch()
            )).execute();

        //更新sku表及其与选项的对应关系表
        List<String> existSkuCodes = new ArrayList<>();
        List<MerchandiseSku> skuToSave = new ArrayList<>();

        for (MerchandiseSkuDTO merchandiseSkuDTO : merchandiseSkuDTOS) {
            String existSkuCode = skuExistBy(merchandiseSkuDTO.getMerchandiseCode(), merchandiseSkuDTO.getOptionCodeAndPriceList());
            if (StringUtils.isEmpty(existSkuCode)) {
                BigDecimal unitPrice = merchandiseSkuDTO.getUnitPrice();
                if (unitPrice == null || unitPrice.compareTo(BigDecimal.ZERO) <= 0) {
                    unitPrice = BigDecimal.valueOf(0.01);
                }
                MerchandiseSku merchandiseSku = new MerchandiseSku();
                String skuCode = CommonUtils.getUUID();
                merchandiseSku.setSkuCode(skuCode);
                merchandiseSku.setMerchandiseCode(merchandiseSkuDTO.getMerchandiseCode());
                merchandiseSku.setUnitPrice(unitPrice);
                merchandiseSku.setPackingFee(merchandiseSkuDTO.getPackingFee());
                merchandiseSku.setStatus(true);
                merchandiseSku.setAddedTimes(0);

                //更新商品sku、选项关系表
                merchandiseSkuDTO.getOptionCodeAndPriceList().forEach(stringStringMap -> skuOptionsRelationshipRepository.saveAll(stringStringMap.entrySet().stream().map(stringStringEntry -> {
                    String attributeName = stringStringEntry.getKey();
                    String optionName = stringStringEntry.getValue();
                    Optional<MerchandiseAttributes> attributesOpt = merchandiseAttributesRepository.findOneByAttributeNameAndStatusAndMerchandiseCode(attributeName, true, merchandiseSkuDTO.getMerchandiseCode());
                    if (!attributesOpt.isPresent()) {
                        throw new CommonException("所选属性无法使用");
                    }
                    Optional<AttributeOptions> optionsOpt = attributeOptionsRepository.findOneByOptionNameAndAttributeCodeAndStatus(optionName, attributesOpt.get().getAttributeCode(), true);
                    if (!optionsOpt.isPresent()) {
                        throw new CommonException("所选选项无法使用");
                    }
                    SkuOptionsRelationship skuOptionsRelationship = new SkuOptionsRelationship();
                    skuOptionsRelationship.setOptionCode(optionsOpt.get().getOptionCode());
                    skuOptionsRelationship.setSkuCode(skuCode);
                    return skuOptionsRelationship;
                }).collect(Collectors.toList())));
                skuToSave.add(merchandiseSku);
            } else {
                existSkuCodes.add(existSkuCode);
            }
        }
        if (existSkuCodes.size() == 0 && merchandiseSkuRepository.findAllByMerchandiseCodeAndStatus(merchandiseSkuDTOS.get(0).getMerchandiseCode(), true).size() > 0) {
            List<String> skuCodeToDelete = jpaQueryFactory.select(qMerchandiseSku.skuCode)
                .from(qMerchandiseSku)
                .where(qMerchandiseSku.merchandiseCode.in(merchandiseSkuDTOS.stream().map(MerchandiseSkuDTO::getMerchandiseCode).collect(Collectors.toList())))
                .where(qMerchandiseSku.status.eq(true))
                .fetch();

            jpaQueryFactory.update(qMerchandiseSku)
                .set(qMerchandiseSku.status, false)
                .where(qMerchandiseSku.skuCode.in(skuCodeToDelete))
                .execute();

            skuOptionsRelationshipRepository.deleteAllBySkuCodeIn(skuCodeToDelete);
        } else {
            List<String> skuCodeToDelete = jpaQueryFactory.select(qMerchandiseSku.skuCode)
                .from(qMerchandiseSku)
                .where(qMerchandiseSku.merchandiseCode.in(merchandiseSkuDTOS.stream().map(MerchandiseSkuDTO::getMerchandiseCode).collect(Collectors.toList())))
                .where(qMerchandiseSku.skuCode.notIn(existSkuCodes))
                .where(qMerchandiseSku.status.eq(true))
                .fetch();

            jpaQueryFactory.update(qMerchandiseSku)
                .set(qMerchandiseSku.status, false)
                .where(qMerchandiseSku.skuCode.in(skuCodeToDelete))
                .execute();

            skuOptionsRelationshipRepository.deleteAllBySkuCodeIn(skuCodeToDelete);
        }

        merchandiseSkuRepository.saveAll(skuToSave);

        return ResponseEntity.ok().build();
    }

    private String skuExistBy(String merchandiseCode, List<Map<String, String>> maps) {
        //根据maps里的属性选项判断该商品是否已有一样的sku
        List<String> skus = jpaQueryFactory.select(qMerchandiseSku.skuCode)
            .from(qMerchandiseSku)
            .where(qMerchandiseSku.merchandiseCode.eq(merchandiseCode)
                .and(qMerchandiseSku.status.eq(true)))
            .fetch();
        Set<String> optionCodeSet = getOptionCodes(merchandiseCode, maps);
        for (String skuCode : skus) {
            Set<String> existOptionCodes = new HashSet<>(
                jpaQueryFactory.select(qSkuOptionsRelationship.optionCode)
                    .from(qSkuOptionsRelationship)
                    .where(qSkuOptionsRelationship.skuCode.eq(skuCode))
                    .fetch()
            );
            if (existOptionCodes.equals(optionCodeSet)) {
                return skuCode;
            }
        }
        return null;
    }

    private Set<String> getOptionCodes(String merchandiseCode, List<Map<String, String>> maps) {
        Set<String> optionCodeSet = new HashSet<>();
        for (Map<String, String> map : maps) {
            for (Map.Entry<String, String> entry : map.entrySet()) {
                Optional<MerchandiseAttributes> attributesOptional =
                    merchandiseAttributesRepository.findOneByAttributeNameAndStatusAndMerchandiseCode(entry.getKey(), true, merchandiseCode);
                if (!attributesOptional.isPresent()) {
                    continue;
                }
                MerchandiseAttributes attributes = attributesOptional.get();
                Optional<AttributeOptions> optionsOptional =
                    attributeOptionsRepository.findOneByOptionNameAndAttributeCodeAndStatus(entry.getValue(), attributes.getAttributeCode(), true);
                if (!optionsOptional.isPresent()) {
                    continue;
                }
                optionCodeSet.add(optionsOptional.get().getOptionCode());
            }
        }
        return optionCodeSet;
    }
}
